#pragma once

#include <algorithm>
#include <cfloat>
#include <atomic>
#include "mltree/entities/points.hpp"

namespace MetricSpaceBenchmark::MLIndex::MLTree {
    // 树节点基类
    template<typename T>
    class MLTreeNode {
    public:
        MLTreeNode() = default;

        virtual ~MLTreeNode() = default;

        MLTreeNode(MLTreeNode &&) noexcept = default;

        MLTreeNode &operator=(MLTreeNode &&) noexcept = default;

        [[nodiscard]] virtual DIS_TYPE area() const = 0;

        [[nodiscard]] virtual std::unique_ptr<MLTreeNode<T>> clone() const = 0;

        virtual void updateCenterAndRadius() = 0;

        virtual void rangeQuery(const Point<T> &queryPoint, DIS_TYPE queryRadius, std::vector<Point<T>> &result) = 0;

        virtual void printNode(size_t &count) const = 0;

        [[nodiscard]] virtual bool isLeaf() const = 0;

        [[nodiscard]] virtual std::unordered_set<Point<T>, PointHash<T>, PointEqual<T>> getPoints() const = 0;

        virtual bool addEntryIfPossible(const Point<T> &entry, unsigned long M) = 0;

        [[nodiscard]] virtual Point<T> getCenter() const = 0;

        [[nodiscard]] virtual DIS_TYPE getRadius() const = 0;
    };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /*
     * 条目信息: id + key + subtree(子树) + cover_radius(覆盖半径) + dis(距离)
     */
    template<typename T>
    class RoutingObject {
    public:
        inline static std::atomic_ulong n_build_ops = 0;
        Point<T> point{};
        DIS_TYPE cover_radius{0.0};
        std::unique_ptr<MLTreeNode<T>> subtree{nullptr};

    public:
        RoutingObject() = default;

        explicit RoutingObject(const Point<T> &point1, const DIS_TYPE cr, std::unique_ptr<MLTreeNode<T>> st = nullptr)
                : point(point1), cover_radius(cr), subtree(std::move(st)) {}

        explicit RoutingObject(const OID_TYPE id, const T &key, const DIS_TYPE d = DBL_MAX, const DIS_TYPE cr = 0.0,
                               std::unique_ptr<MLTreeNode<T>> st = nullptr) : point(id, key, d), cover_radius(cr),
                                                                              subtree(std::move(st)) {}

        explicit RoutingObject(const OID_TYPE id, T &&k, const DIS_TYPE d = DBL_MAX, const DIS_TYPE cr = 0.0,
                               std::unique_ptr<MLTreeNode<T>> s = nullptr) : point(id, std::forward<T>(k), d),
                                                                             cover_radius(cr), subtree(std::move(s)) {}

        // 显式定义拷贝构造函数
        RoutingObject(const RoutingObject &other)
                : point(other.point), cover_radius(other.cover_radius),
                  subtree(other.subtree ? other.subtree->clone() : nullptr) {

            RoutingObject<T>::n_build_ops++;
        }

        RoutingObject &operator=(const RoutingObject &other) {
            if (this != &other) {
                point = other.point;
                cover_radius = other.cover_radius;
                subtree = other.subtree ? other.subtree->clone() : nullptr;
            }

            return *this;
        }

        RoutingObject(RoutingObject &&other) noexcept: point(std::move(other.point)), cover_radius(other.cover_radius),
                                                       subtree(std::move(other.subtree)) {}


        RoutingObject &operator=(RoutingObject &&other) noexcept {
            if (this != &other) {
                point = std::move(other.point);
                cover_radius = other.cover_radius;
                subtree = std::move(other.subtree);
                other.subtree = nullptr;
            }
            return *this;
        }

        DIS_TYPE distance(const Point<T> &otherPoint) const {
            RoutingObject<T>::n_build_ops++;
            return point.distance(otherPoint);
        }
    };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /*
     * 条目信息: id + key +dis
     */
    template<typename T>
    class DBEntry {
    public:
        inline static std::atomic_ulong n_query_ops = 0;

        Point<T> point;

        DBEntry() = default;

        explicit DBEntry(const Point<T> &point1) : point(point1) {}

        explicit DBEntry(const OID_TYPE id, const T &key, const DIS_TYPE dis = DBL_MAX) : point(id, key, dis) {}

        explicit DBEntry(const OID_TYPE id, T &&k, const DIS_TYPE d = DBL_MAX) : point(id, std::forward<T>(k), d) {}


        DBEntry(const DBEntry &other) { point = other.point; }

        DBEntry &operator=(const DBEntry &other) {
            if (this != &other) {
                point = other.point;
            }
            return *this;
        }

        DBEntry(DBEntry &&other) noexcept: point(std::move(other.point)) {}

        DBEntry &operator=(DBEntry &&other) noexcept {
            if (this != &other) {
                point = std::move(other.point);
            }
            return *this;
        }

        [[nodiscard]] DIS_TYPE distance(const Point<T> &otherPoint) const {
            DBEntry<T>::n_query_ops++;
            return point.distance(otherPoint);
        }
    };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // 线性模型 LM, 包含斜率和截距
    template<typename T>
    struct LinearModel {
        double slope;
        double intercept;

        explicit LinearModel(double s = 0.0, double i = 0.0) : slope(s), intercept(i) {}
    };

    // 学习索引节点 ILM 的条目结构
    template<typename T>
    class ILMEntry {
    public:
        Point<T> m_ci;  // 对应节点的中心点
        DIS_TYPE m_ri;  // 原始 M 树中节点 ci 的覆盖半径
        std::unique_ptr<MLTreeNode<T>> m_subtree;  // 指向子节点的指针
        DIS_TYPE m_MinDi;  // ci 到枢轴点 p 的最小距离
        DIS_TYPE m_MaxDi;  // ci 到枢轴点 p 的最大距离

        ILMEntry(const Point<T> &c, double r, std::unique_ptr<MLTreeNode<T>> p, double minD, double maxD)
                : m_ci(c), m_ri(r), m_subtree(std::move(p)), m_MinDi(minD), m_MaxDi(maxD) {}
    };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}